package org.convallaria.infrastruct.rg.health;

import lombok.extern.slf4j.Slf4j;
import org.convallaria.infrastruct.rg.config.RegistryProperties;
import org.convallaria.infrastruct.rg.exception.HealthCheckException;
import org.convallaria.infrastruct.rg.monitor.RegistryMetrics;
import org.convallaria.infrastruct.rg.util.ThreadPoolManager;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import io.micrometer.core.instrument.Timer;
import jakarta.annotation.PreDestroy;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 服务健康检查器
 * 提供服务的健康检查功能
 * 
 * @author gjh
 * @since 2025-01-27
 */
@Slf4j
@Component
public class ServiceHealthChecker {
    
    private final RestTemplate restTemplate;
    private final RegistryProperties registryProperties;
    private final RegistryMetrics registryMetrics;
    private ExecutorService executorService;
    
    public ServiceHealthChecker(RestTemplate restTemplate, RegistryProperties registryProperties, RegistryMetrics registryMetrics) {
        this.restTemplate = restTemplate;
        this.registryProperties = registryProperties;
        this.registryMetrics = registryMetrics;
        
        // 使用企业级线程池管理器创建健康检查线程池
        String poolName = "health-check-pool";
        int threadPoolSize = registryProperties.getHealthCheck().getThreadPoolSize();
        
        // 检查是否已存在同名线程池，如果存在则获取，否则创建新的
        this.executorService = ThreadPoolManager.getThreadPool(poolName);
        if (this.executorService == null) {
            this.executorService = ThreadPoolManager.createFixedThreadPool(
                    poolName, 
                    threadPoolSize, 
                    true  // 设置为守护线程
            );
            log.info("健康检查线程池已创建: {}, 线程数: {}", poolName, threadPoolSize);
        } else {
            log.info("使用已存在的健康检查线程池: {}", poolName);
        }
    }
    
    /**
     * 检查服务实例健康状态
     * 
     * @param serviceInstance 服务实例
     * @return 是否健康
     */
    @Retryable(value = {Exception.class}, maxAttempts = 2, backoff = @Backoff(delay = 1000))
    public boolean checkInstanceHealth(ServiceInstance serviceInstance) {
        if (!registryProperties.getHealthCheck().isEnabled()) {
            log.debug("健康检查功能已禁用");
            return true;
        }
        
        Timer.Sample sample = registryMetrics.startHealthCheckTimer();
        try {
            String healthUrl = buildHealthUrl(serviceInstance);
            log.debug("检查服务实例健康状态: {}", healthUrl);
            
            boolean isHealthy;
            if (registryProperties.getHealthCheck().isAsyncEnabled()) {
                // 异步检查健康状态
                CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
                    try {
                        restTemplate.getForObject(healthUrl, String.class);
                        return true;
                    } catch (Exception e) {
                        log.warn("服务实例健康检查失败: {} - {}", healthUrl, e.getMessage());
                        return false;
                    }
                }, executorService);
                
                // 设置超时时间
                isHealthy = future.get(
                        registryProperties.getHealthCheck().getTimeout().toMillis(), 
                        TimeUnit.MILLISECONDS
                );
            } else {
                // 同步检查健康状态
                restTemplate.getForObject(healthUrl, String.class);
                isHealthy = true;
            }
            
            // 记录监控指标
            if (isHealthy) {
                registryMetrics.recordHealthCheckSuccess(
                        serviceInstance.getServiceId(), 
                        serviceInstance.getInstanceId()
                );
            } else {
                registryMetrics.recordHealthCheckFailure(
                        serviceInstance.getServiceId(), 
                        serviceInstance.getInstanceId(), 
                        "健康检查失败"
                );
            }
            
            return isHealthy;
            
        } catch (Exception e) {
            String errorMsg = "服务实例健康检查异常: " + serviceInstance.getUri();
            log.error(errorMsg, e);
            registryMetrics.recordHealthCheckFailure(
                    serviceInstance.getServiceId(), 
                    serviceInstance.getInstanceId(), 
                    e.getMessage()
            );
            throw new HealthCheckException(errorMsg, serviceInstance.getServiceId(), e);
        } finally {
            registryMetrics.stopHealthCheckTimer(sample, serviceInstance.getServiceId(), serviceInstance.getInstanceId());
        }
    }
    
    /**
     * 销毁时关闭线程池
     * 注意：由于使用了ThreadPoolManager统一管理，这里不需要手动关闭
     * ThreadPoolManager会在JVM关闭时自动清理所有线程池
     */
    @PreDestroy
    public void destroy() {
        // 由于使用了ThreadPoolManager统一管理线程池，这里不需要手动关闭
        // ThreadPoolManager会在JVM关闭时通过shutdown hook自动清理所有线程池
        log.info("ServiceHealthChecker正在销毁，线程池将由ThreadPoolManager统一管理");
        
        // 可选：如果需要立即关闭特定线程池，可以使用以下代码
        // ThreadPoolManager.shutdownPool("health-check-pool");
    }
    
    /**
     * 批量检查服务实例健康状态
     * 
     * @param serviceInstances 服务实例列表
     * @return 健康实例列表
     */
    public List<ServiceInstance> checkInstancesHealth(List<ServiceInstance> serviceInstances) {
        if (serviceInstances == null || serviceInstances.isEmpty()) {
            return List.of();
        }
        
        return serviceInstances.parallelStream()
                .filter(this::checkInstanceHealth)
                .toList();
    }
    
    /**
     * 更新Nacos中的服务实例健康状态
     * 
     * @param serviceName 服务名称
     * @param ip 实例IP
     * @param port 实例端口
     * @param healthy 健康状态
     */
    public void updateInstanceHealthInNacos(String serviceName, String ip, int port, boolean healthy) {
        // 由于 namingServiceInstance() 方法已弃用，建议使用 Spring Cloud 原生的健康检查机制
        log.warn("updateInstanceHealthInNacos() 方法已简化，建议使用 Spring Cloud 原生的健康检查机制");
        log.info("健康状态检查: {}:{}:{} -> {}", serviceName, ip, port, healthy);
    }
    
    /**
     * 获取服务实例的健康检查URL
     * 
     * @param serviceInstance 服务实例
     * @return 健康检查URL
     */
    private String buildHealthUrl(ServiceInstance serviceInstance) {
        String scheme = serviceInstance.getScheme();
        if (scheme == null) {
            scheme = "http";
        }
        
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String contextPath = serviceInstance.getMetadata().get("context-path");
        if (contextPath == null) {
            contextPath = "";
        }
        
        return String.format("%s://%s:%d%s/actuator/health", scheme, host, port, contextPath);
    }
    
    /**
     * 检查服务是否可达
     * 
     * @param serviceInstance 服务实例
     * @return 是否可达
     */
    public boolean isServiceReachable(ServiceInstance serviceInstance) {
        try {
            String pingUrl = buildPingUrl(serviceInstance);
            restTemplate.getForObject(pingUrl, String.class);
            return true;
        } catch (Exception e) {
            log.debug("服务不可达: {} - {}", serviceInstance.getUri(), e.getMessage());
            return false;
        }
    }
    
    /**
     * 获取服务实例的ping URL
     * 
     * @param serviceInstance 服务实例
     * @return ping URL
     */
    private String buildPingUrl(ServiceInstance serviceInstance) {
        String scheme = serviceInstance.getScheme();
        if (scheme == null) {
            scheme = "http";
        }
        
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String contextPath = serviceInstance.getMetadata().get("context-path");
        if (contextPath == null) {
            contextPath = "";
        }
        
        return String.format("%s://%s:%d%s/actuator/health/liveness", scheme, host, port, contextPath);
    }
    
    /**
     * 获取服务实例的详细信息
     * 
     * @param serviceInstance 服务实例
     * @return 实例信息
     */
    public ServiceInstanceInfo getInstanceInfo(ServiceInstance serviceInstance) {
        ServiceInstanceInfo info = new ServiceInstanceInfo();
        info.setServiceId(serviceInstance.getServiceId());
        info.setInstanceId(serviceInstance.getInstanceId());
        info.setHost(serviceInstance.getHost());
        info.setPort(serviceInstance.getPort());
        info.setUri(serviceInstance.getUri().toString());
        info.setMetadata(serviceInstance.getMetadata());
        info.setHealthy(checkInstanceHealth(serviceInstance));
        info.setReachable(isServiceReachable(serviceInstance));
        info.setCheckTime(System.currentTimeMillis());
        
        return info;
    }
    
    /**
     * 服务实例信息
     */
    public static class ServiceInstanceInfo {
        private String serviceId;
        private String instanceId;
        private String host;
        private int port;
        private String uri;
        private java.util.Map<String, String> metadata;
        private boolean healthy;
        private boolean reachable;
        private long checkTime;
        
        // Getters and Setters
        public String getServiceId() { return serviceId; }
        public void setServiceId(String serviceId) { this.serviceId = serviceId; }
        
        public String getInstanceId() { return instanceId; }
        public void setInstanceId(String instanceId) { this.instanceId = instanceId; }
        
        public String getHost() { return host; }
        public void setHost(String host) { this.host = host; }
        
        public int getPort() { return port; }
        public void setPort(int port) { this.port = port; }
        
        public String getUri() { return uri; }
        public void setUri(String uri) { this.uri = uri; }
        
        public java.util.Map<String, String> getMetadata() { return metadata; }
        public void setMetadata(java.util.Map<String, String> metadata) { this.metadata = metadata; }
        
        public boolean isHealthy() { return healthy; }
        public void setHealthy(boolean healthy) { this.healthy = healthy; }
        
        public boolean isReachable() { return reachable; }
        public void setReachable(boolean reachable) { this.reachable = reachable; }
        
        public long getCheckTime() { return checkTime; }
        public void setCheckTime(long checkTime) { this.checkTime = checkTime; }
    }
}
